// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rainbowchat.http.logic;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.imageio.ImageIO;

import com.eva.epc.common.util.CommonUtils;
import com.eva.framework.dbpool.DBShell;
import com.eva.framework.utils.LoggerFactory;
import com.x52im.rainbowchat.BaseConf;

/**
 * 群组头像生成辅助类（生成逻辑与微信保持一致：4人以内生成4宫格、9人以内生成9宫格等）。
 * <p>
 * <b>关于性能的说明：</b>
 * 因为生成群头像时，需要读取群员头像文件并写入最络群组头像文件，因而在用户量较大时，
 * 这些磁盘操作都有可能带来性能瓶颈，所以建议如果是性能优先的im系统里可以考虑不要自
 * 动后成群头像，否则请评估并考虑将这样的功能拆分成独立的服务，从而不至于影响主业
 * 务服务器的性能.
 * 
 * @author chuan_bai(https://www.jianshu.com/p/394f9b0357b3)
 * @author Jack Jiang
 * @since 4.3
 */
public final class GroupAvatarHelper 
{
	/**
	 * 图片的间隙
	 */
	private static final int SIDE = 6;
	/**
	 * 画板尺寸
	 */
	private static final int CANVANS_W = 112;
	private static final int CANVANS_H = 112;

	/**
	 * 尺寸1 （小）
	 */
	private static final int ONE_IMAGE_SIZE = CANVANS_H - (2 * SIDE);
	/**
	 * 尺寸2 （中）
	 */
	private static final int TWO_IMAGE_SIZE = (CANVANS_H - (3 * SIDE)) / 2;
	/**
	 * 尺寸3 （大）
	 */
	private static final int FIVE_IMAGE_SIZE = (CANVANS_H - (4 * SIDE)) / 3;
	
	private static BufferedImage defaultGroupMemberAvatar = null;

	/**
	 * 一站式生成一个群组的群头像（本方法将自动查询该群内的前9个成员头像并拼合出完整群头像的全部逻辑）。
	 * 
	 * @param groupId 要生成群头像的群组id
	 * @return true表示生成成功，否则表示过程中出错了（没有成功生成）
	 * @see #getCombinationOfhead
	 */
	public static boolean generateGroupAvatar(String groupId)
	{
		LoggerFactory.getLog().debug("【生成群聊头像-START】群组"+groupId+"的头像生成马上开始。。。。。。");
		
		long t = System.currentTimeMillis();
		
		boolean sucess = false;
		try
		{
			// 当上次拼合群头像时，使用的群员头像数
			String[] cnts = GroupChatProcessor.db.querySingleRowItems(
					"select g_member_count,avatar_include_cnt from group_base" +" where g_id='"+groupId+"'");
			
			int current_group_member_count = CommonUtils.getIntValue(cnts[0], 0);
			int last_avatar_include_cnt = CommonUtils.getIntValue(cnts[1], 0);
			// 当当前群成员大于9 且 上次拼合群成员头像时用的头像数已经足9时，就不需要重新
			// 生成群头像了，这样就能节省服务端的资源（不然反复生成群头像，很不经济）
			if(current_group_member_count > 9 && last_avatar_include_cnt >= 9)
			{
				LoggerFactory.getLog().debug("【生成群聊头像-END】群组"+groupId
						+"的头像无需再次生成，原因：（当前群员总数="+current_group_member_count
						+", 上次拼合使用头像数="+last_avatar_include_cnt+"）。");
				return false;
			}
			
			// 当群不足9人 或上次拼合群成员头像时用的头像数不足9人时，就需
			// 要重新生成群头像，从而保证群头像能与当前实际的群人数保持一致。
			// 总之生成原则就是：当群小于9人或上次拼合群成员头像时用的头像数不足9人时，就重新生新群头像！
			
			LoggerFactory.getLog().debug("【生成群聊头像-continue】群组"+groupId
					+"的头像将继续生成>>> ，原因：（当前群员总数="+current_group_member_count
						+", 上次拼合使用头像数="+last_avatar_include_cnt+"）。");
			
			StringBuilder sb = new StringBuilder()
			.append("select ");

			// SQLServer数据库中取前9个用户数据的方法（即在SQL语句前面加上top语句）
			if(DBShell.isSQLServer())
				sb.append(" top 9 ");

			sb.append("	 b.user_avatar_file_name ")
			.append("FROM ")
			/** 使用子查询提升查询性能 */
			.append("(")
			.append("	select user_uid,g_id,nickname_ingroup from group_members where g_id='"+groupId+"' ")
			.append(") a ")
			.append("left join missu_users b on a.user_uid=b.user_uid ")
			.append("order by b.user_avatar_file_name desc ");

			// MySQL数据库中取前9个用户数据的方法（即在SQL语句末尾加上linit语句）
			if(DBShell.isMySQL())
				sb.append(" limit 0,9 ");

			// 查询该群中9个用户的头像文件名（不足9个则返回实际数据
			// ，大于9个的群则尽最大可能返回排序后的9个有头像的人）
			Vector<Vector> originalVecs = GroupChatProcessor.db.queryData(sb.toString());
			
			LoggerFactory.getLog().debug("【生成群聊头像-第1/2步】群组"+groupId+"的前9个用户查询完成(实取数量："+originalVecs.size()+"人)，此步共耗时："+(System.currentTimeMillis()-t)+"毫秒。");
			
			if(originalVecs.size() > 0)
			{
				ArrayList<String> al = new ArrayList<String>();
				for(Vector r : originalVecs)
				{
					String avartarFileName = (String)r.get(0);
					al.add(avartarFileName);
				}
				
				// 开始拼合头像并保存到磁盘
				boolean combinateSucess = GroupAvatarHelper.getCombinationOfhead(al, groupId);
				
				if(combinateSucess)
				{
					try
					{
						// 更新数据中本次拼合头像的人数
						GroupChatProcessor.db.update("update group_base set avatar_include_cnt="
							+al.size()+" where g_id=?", new Object[]{groupId}, true);
					}
					catch (Exception e)
					{
						LoggerFactory.getLog().warn(e.getMessage(), e);
					}
				}
				
				sucess = combinateSucess;
			}
			else
			{
				LoggerFactory.getLog().warn("【生成群聊头像】群组"+groupId+"的群员数是0，无需生成群头像！");
			}
		}
		catch (Exception e)
		{
			LoggerFactory.getLog().warn("【群聊头像】生成群"+groupId+"的头像时出错了", e);
		}
		
		LoggerFactory.getLog().debug("【生成群聊头像-END】群组"+groupId+"的头像生成已完成（成功?"
				+sucess+"），两步总耗时共："+(System.currentTimeMillis()-t)+"毫秒。");
		
		return sucess;
	}
	
	/**
	 * 获得默认的成员头像（这是在极端情况下读不到真正的头像文件时可以使用）。
	 * 
	 * @return
	 */
	private static BufferedImage getDefaultGroupMemberAvatar()
	{
		if(defaultGroupMemberAvatar == null)
		{
			try
			{
				// 缓存起提高性能
				defaultGroupMemberAvatar = ImageIO.read(
						GroupAvatarHelper.class.getResourceAsStream("resource/default_group_member_avatar.png"));
			}
			catch (Exception e)
			{
				LoggerFactory.getLog().warn(e.getMessage(), e);
			}
		}
		return defaultGroupMemberAvatar;
	}

	/**
	 * 用指定的用户头像文件集合，拼合出最终的RainbowChat的群组头像。
	 *
	 * @param membersAvatarFileNames   群员的头像文件链接
	 * @param dir     输出路径
	 * @param groupId 群编号
	 * @return
	 * @throws IOException
	 */
	private static boolean getCombinationOfhead(List<String> membersAvatarFileNames
			, String groupId) throws Exception 
	{
		LoggerFactory.getLog().debug("【生成群聊头像-第2/2步】群组"+groupId+"的群头像拼合马上开始 ....");
		
		long t = System.currentTimeMillis();
		
		List<BufferedImage> bufferedImages = new ArrayList<BufferedImage>();

		int imageSize = 0;
		if (membersAvatarFileNames.size() <= 1)
		{
			//若为一张图片
			imageSize = ONE_IMAGE_SIZE;
		} 
		else if (membersAvatarFileNames.size() > 1 && membersAvatarFileNames.size() < 5)
		{
			//若为2-4张图片

			imageSize = TWO_IMAGE_SIZE;
		} 
		else 
		{
			//若>=5张图片
			imageSize = FIVE_IMAGE_SIZE;
		}

		for (int i = 0; i < membersAvatarFileNames.size(); i++) 
		{
			// 本次要读取的图片路径
			String path = null;
			
			String avatarFileName = membersAvatarFileNames.get(i);
			
			// 如果传进来的头像为空，则表示将使用默认头像，resize2方法中会自动处理
			if(!CommonUtils.isStringEmpty(avatarFileName, true))
			{
				// 头像缩略图文件路径
				String avatarThumbFilePath = BaseConf.getInstance().getDIR_USER_AVATAR_UPLOAD()+"th_"+avatarFileName;
				// 头像文件路径
				String avatarFilePath = BaseConf.getInstance().getDIR_USER_AVATAR_UPLOAD()+avatarFileName;
				// 优先使用头像缩略图来拼合群聊头像，这样能提高服务端性能和减少资源占用，
				// 只在缩略图不存在时使用头像文件原图（不存在缩略图的情况可能是RainbowChat4.3
				// 以前的系统中没有生成用户头像，也可能是用户头像上传时没有成功生成缩略图等）
				if(!new File(avatarThumbFilePath).exists())
					path = avatarFilePath;
				else
					path = avatarThumbFilePath;
			}
			
			BufferedImage resize2 = GroupAvatarHelper.resize2(path, imageSize, imageSize, true);
			bufferedImages.add(resize2);
		}

		BufferedImage outImage = new BufferedImage(CANVANS_W, CANVANS_H, BufferedImage.TYPE_INT_RGB);

		// 生成画布
		Graphics g = outImage.getGraphics();
		Graphics2D g2d = (Graphics2D) g;
		// 设置背景色
		g2d.setBackground(new Color(219, 223, 225));
		// 通过使用当前绘图表面的背景色进行填充来清除指定的矩形。
		g2d.clearRect(0, 0, CANVANS_W, CANVANS_H);

		// 开始拼凑 根据图片的数量判断该生成那种样式的组合头像目前为九种
		for (int i = 1; i <= bufferedImages.size(); i++)
		{
			Integer size = bufferedImages.size();
			
			switch (size) 
			{
				case 1:
				{
					g2d.drawImage(bufferedImages.get(i - 1), SIDE, SIDE, null);
					break;
				}
				case 2:
				{
					if (i == 1) 
						g2d.drawImage(bufferedImages.get(i - 1), SIDE, (CANVANS_W - imageSize) / 2, null);
					else 
						g2d.drawImage(bufferedImages.get(i - 1), 2 * SIDE + imageSize, (CANVANS_W - imageSize) / 2, null);

					break;
				}
				case 3:
				{
					if (i == 1) 
						g2d.drawImage(bufferedImages.get(i - 1), (CANVANS_W - imageSize) / 2, SIDE, null);
					else
						g2d.drawImage(bufferedImages.get(i - 1), (i - 1) * SIDE + (i - 2) * imageSize, imageSize + (2 * SIDE), null);
					
					break;
				}
				case 4:
				{
					if (i <= 2) 
						g2d.drawImage(bufferedImages.get(i - 1), i * SIDE + (i - 1) * imageSize, SIDE, null);
					else 
						g2d.drawImage(bufferedImages.get(i - 1), (i - 2) * SIDE + (i - 3) * imageSize, imageSize + 2 * SIDE, null);
					
					break;
				}
				case 5:
				{
					if (i <= 2) 
						g2d.drawImage(bufferedImages.get(i - 1), (CANVANS_W - 2 * imageSize - SIDE) / 2 + (i - 1) * imageSize + (i - 1) * SIDE, (CANVANS_W - 2 * imageSize - SIDE) / 2, null);
					else 
						g2d.drawImage(bufferedImages.get(i - 1), (i - 2) * SIDE + (i - 3) * imageSize, ((CANVANS_W - 2 * imageSize - SIDE) / 2) + imageSize + SIDE, null);
					
					break;
				}
				case 6:
				{
					if (i <= 3) 
						g2d.drawImage(bufferedImages.get(i - 1), SIDE * i + imageSize * (i - 1), (CANVANS_W - 2 * imageSize - SIDE) / 2, null);
					else
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 3) * SIDE) + ((i - 4) * imageSize), ((CANVANS_W - 2 * imageSize - SIDE) / 2) + imageSize + SIDE, null);
					
					break;
				}
				case 7:
				{
					if (i <= 1) 
						g2d.drawImage(bufferedImages.get(i - 1), 2 * SIDE + imageSize, SIDE, null);
					
					if (i <= 4 && i > 1) 
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 1) * SIDE) + ((i - 2) * imageSize), 2 * SIDE + imageSize, null);
					
					if (i <= 7 && i > 4) 
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 4) * SIDE) + ((i - 5) * imageSize), 3 * SIDE + 2 * imageSize, null);
					
					break;
				}
				case 8:
				{
					if (i <= 2) 
						g2d.drawImage(bufferedImages.get(i - 1), (CANVANS_W - 2 * imageSize - SIDE) / 2 + (i - 1) * imageSize + (i - 1) * SIDE, SIDE, null);
					
					if (i <= 5 && i > 2) 
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 2) * SIDE) + ((i - 3) * imageSize), 2 * SIDE + imageSize, null);
					
					if (i <= 8 && i > 5) 
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 5) * SIDE) + ((i - 6) * imageSize), 3 * SIDE + 2 * imageSize, null);
					
					break;
				}
				case 9:
				{
					if (i <= 3) 
						g2d.drawImage(bufferedImages.get(i - 1), (i * SIDE) + ((i - 1) * imageSize), SIDE, null);
					
					if (i <= 6 && i > 3) 
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 3) * SIDE) + ((i - 4) * imageSize), 2 * SIDE + imageSize, null);
				
					if (i <= 9 && i > 6) 
						g2d.drawImage(bufferedImages.get(i - 1), ((i - 6) * SIDE) + ((i - 7) * imageSize), 3 * SIDE + 2 * imageSize, null);
					
					break;
				}
				default:
					break;
			}
		}

		// 拼合后的群聊头像保存路径
		StringBuffer outPath = new StringBuffer().append(BaseConf.getInstance().getDIR_USER_AVATARGROUP_DIR())
				.append(groupId).append(".jpg"); // 生成的群组图片名形如”000000001.jpg“

		String format = "JPG";
		File file = new File(outPath.toString());
		if (!file.exists()) {
			file.mkdirs();
		}
		
		// 保存群头像
		boolean sucess = ImageIO.write(outImage, format, file);
		
		LoggerFactory.getLog().debug("【群聊头像-第2/2步】群组"+groupId+"的头像拼合完毕(成功了?"+sucess+")，此步共耗时："+(System.currentTimeMillis()-t)+"毫秒。");
		
		return sucess;
	}

	/**
	 * 图片缩放。
	 *
	 * @param filePath 图片路径（支持网络图片）
	 * @param height   高度
	 * @param width    宽度
	 * @param bb       比例不对时是否需要补白
	 */
	private static BufferedImage resize2(String filePath, int height, int width, boolean bb)
	{
		try 
		{
			double ratio = 0; // 缩放比例
//          System.out.println("图片缩放"+filePath);
			BufferedImage bi = null;
			
			// 进行容错，保证在读不到图片数据时余下代码还能按照正常算法走完
			if(!CommonUtils.isStringEmpty(filePath, true))
			{
				try
				{
					if (filePath.indexOf("http://") == 0) 
						bi = ImageIO.read(new URL(filePath));
					else
						bi = ImageIO.read(new File(filePath));
				}
				catch (Exception e)
				{
					LoggerFactory.getLog().warn(e.getMessage()+"("+filePath+")", e);
				}
			}
			
			// 极端情况下读不到图片数据的话，至少给个默认的
			if(bi == null)
				bi = getDefaultGroupMemberAvatar();
			
			Image itemp = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);
			// 计算比例
			if ((bi.getHeight() > height) || (bi.getWidth() > width))
			{
				if (bi.getHeight() > bi.getWidth())
					ratio = (new Integer(height)).doubleValue()/ bi.getHeight();
				else 
					ratio = (new Integer(width)).doubleValue() / bi.getWidth();

				AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
				itemp = op.filter(bi, null);
			}
			if (bb)
			{
				BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
				Graphics2D g = image.createGraphics();
				g.setColor(Color.white);
				g.fillRect(0, 0, width, height);
				
				if (width == itemp.getWidth(null))
				{
					g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2,
							itemp.getWidth(null), itemp.getHeight(null),
							Color.white, null);
				} 
				else 
				{
					g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0,
							itemp.getWidth(null), itemp.getHeight(null),
							Color.white, null);
				}
				g.dispose();
				itemp = image;
			}
			return (BufferedImage) itemp;
		} 
		catch (Exception e)
		{
			LoggerFactory.getLog().warn(e.getMessage()+"("+filePath+")", e);
		}
		
		return null;
	}

//	/**
//	 * 可依次生成九种情况的群头像
//	 *
//	 * @param args
//	 * @throws IOException
//	 */
//	public static void main(String[] args) throws Exception
//	{
//		DBShell.DB_TYPE = DBShell.DBType.mySql;
//		
//		String groupId = "0000000155";
//		
////		List<String> list = new ArrayList<String>();
////		list.add("400070_79196464a7a8bd4103eff494e0d4cd67.jpg");
////		list.add("a.jpg");
////		list.add("400092_4f330a9bcf1d22054364c119ceb8b753.jpg");
////		list.add(null);
////		list.add("400088_1d88c9fea46d09692828b5251f420db0.jpg");
////		list.add("");
////		list.add("400096_76dfc2ab1f57af24a495b25dce1395dc.jpg");
////		list.add("400095_c2ea8d7a74a96b329b5d84cd21208f9d.jpg");
////		GroupAvatarHelper.getCombinationOfhead(list, groupId.toString());
//		
//		GroupAvatarHelper.generateGroupAvatar(groupId);
//	}
}
